package interfaces.menu;

import interfaces.GUISource;
import interfaces.ImageUpdater;
import interfaces.language.LanguageButton;
import interfaces.menu.joinGame.gamesTable.GamesTable;
import interfaces.options.ComboBoxOptionWidget;
import interfaces.options.OptionWidget;
import interfaces.options.OptionWidgetFactory;
import interfaces.options.OptionsContainer;
import interfaces.options.TextEditorOptionWidget;
import interfaces.options.TextInputError;
import interfaces.options.TextInputErrorWindow;
import interfaces.tables.ChoiceContainer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import logic.common.game.GameMode;
import logic.nodes.nodeSettings.Settings;
import main.GameCycle;
import main.InitGame;
import main.listeners.listenerClasses.LanguageListener;
import map.infos.MapDescription;
import map.infos.MapInfos;
import networking.MasterServerInterface;

import org.fenggui.decorator.background.PlainBackground;
import org.fenggui.event.ButtonPressedEvent;
import org.fenggui.event.IButtonPressedListener;
import org.fenggui.event.ISelectionChangedListener;
import org.fenggui.event.SelectionChangedEvent;

import ai.AILevel;
import fileHandling.MapLoader;
import fileHandling.SettingsLoader;
import fileHandling.language.Lang;
import fileHandling.language.LanguageLoader;
import fileHandling.language.interfaces.MenuText;
import fileHandling.language.interfaces.NewGameText;
import fileHandling.language.options.Filters;
import fileHandling.language.options.OptionValues;

public class CreateGameContent extends MenuContent {
	
	private static final String DEFAULT_SETTINGS_FILE = "defaultSettings.xml";
	private static final String DEFAULT_SETTINGS_PATH = MapLoader.MAPS_PATH + DEFAULT_SETTINGS_FILE;
	
	private static final String STANDARD_MAP = "StandardMap";
	
	private static final Lang RESPAWN = NewGameText.Respawn_Time;
	private static final Lang ENDCOUNTDOWN = NewGameText.End_Countdown;
	private static final Lang MAXPLAYERS = NewGameText.Max_Players;
	private static final Lang GAMEMODE = NewGameText.Game_Mode;
	private static final Lang BOTS = NewGameText.Enable_Bots;
	private static final Lang AI_LEVEL = NewGameText.AI_Level;
	private static final Lang ONLINE = NewGameText.Online;
	private static final Lang SERVERNAME = NewGameText.Server_Name;
	private static final Lang SCORE_LIMIT = NewGameText.Score_Limit;
	private static final Lang TICKETS = NewGameText.Tickets;
	private static final Lang BALANCEDTEAMS = NewGameText.Balanced_Teams;
	private static final Lang FRIENDLYFIRE = Filters.Friendly_Fire;
	
	private static final Lang START_GAME = NewGameText.Start_Game;
	
	private LanguageButton gameButton, saveButton, detailsButton;
	private boolean detailsButtonSet;
	
	private String currentMapName;
	private DetailsWindow detailsWindow;
	
	private ChoiceContainer<MapTableButton> mapChoice;
	private Collection<String> foundScenarios;
	private HashMap<String, MapDescription> mapDescriptions;
	private HashMap<String, String> mapNames;
	
	private ImageUpdater previewImage;
	
	private TextEditorOptionWidget scoreLimitOption, ticketsOption;
	private OptionsContainer options;
	private OptionWidgetFactory widgetFactory;
	private Settings defaultSettings;
	
	public CreateGameContent(AbsMenu menu) {
		super(menu);
		
		mapNames = new HashMap<String, String>();
		foundScenarios = MapLoader.findMaps();
		
		mapDescriptions = new HashMap<String, MapDescription>();
		for(String mapName : foundScenarios) {
			MapDescription description = MapLoader.getMapDescription(mapName);
			if(description != null) {
				mapDescriptions.put(mapName, description);
				MapInfos infos = MapLoader.getMapInfos(mapName);
				mapNames.put(infos.getMapName(), mapName);
			}
		}
				
		makeMapChoice();
		makeGameSettings();
		makeGameButtons();
		makePreview();
		
		InitGame.get().addLanguageListener(new LanguageListener() {
			@Override
			public void languageChanged() {
				changeLanguage();
			}
		});
		
		String standardMaps = defaultSettings.getValueOf(STANDARD_MAP);
		if(standardMaps != null && standardMaps != "") {
			String[] names = standardMaps.split(",");
			for(String mapName : names) {
				for(MapTableButton b : mapChoice.getChoiceButtons()) {
					if(b.getText().equals(mapName)) {
						MapTableButton newButton = (MapTableButton)b.getCopy();
						mapChoice.addSelectedButton(newButton);
						
						mapChoice.setSelectedRemoveButton(newButton);
						
						break;
					}
				}
			}
			
			String prevPath = MapLoader.MAPS_PATH + mapNames.get(names[0]) + "/preview.png";
			previewImage.setImage(prevPath);
			enableGameButton(true);
			
			currentMapName = names[0];
			addWidget(detailsButton);
			detailsButtonSet = true;
			
			mapChoice.addWidget(mapChoice.getRemoveButton());
		}
		
		layout();
	}
	
	private void makeMapChoice() {
		int choiceWidth = getWidth();
		int choiceHeight = getHeight() / 3;
		Lang choices = NewGameText.Installed_Maps;
		Lang selection = NewGameText.Selected_Maps;
		int choiceY = getHeight() / 3 * 2;
		mapChoice = new ChoiceContainer<MapTableButton>(choiceWidth, choiceHeight, 0, choiceY, choices, selection, 0);
		mapChoice.changeButtonTexts(LanguageLoader.get(NewGameText.Add_Map), LanguageLoader.get(NewGameText.Remove_Map));
		mapChoice.getAppearance().add(new PlainBackground(GamesTable.GAMES_TABLE_BG_COLOR));
		
		mapChoice.addListenerToAddButton(new IButtonPressedListener() {
			@Override
			public void buttonPressed(ButtonPressedEvent e) {
				if(!gameButton.isEnabled()) enableGameButton(true);
			}
		});
		
		mapChoice.addListenerToRemoveButton(new IButtonPressedListener() {
			@Override
			public void buttonPressed(ButtonPressedEvent e) {
				if(mapChoice.getSelection().isEmpty()) enableGameButton(false);
			}
		});
		
		for(final String mapName : mapNames.keySet()) {
			final String mapDirName = mapNames.get(mapName);
			IButtonPressedListener listener = new IButtonPressedListener() {
				@Override
				public void buttonPressed(ButtonPressedEvent e) {
					String prevPath = MapLoader.MAPS_PATH + mapDirName + "/preview.png";
					previewImage.setImage(prevPath);
					
					currentMapName = mapName;
					if(!detailsButtonSet) {
						addWidget(detailsButton);
						detailsButtonSet = true;
					}
				}
			};
			MapTableButton scenarioButton = new MapTableButton(mapName, mapDirName, listener, GUISource.bigFont);
			GUISource.loadTexture(MapLoader.MAPS_PATH + mapDirName + "/preview.png");
			mapChoice.addChoiceButton(scenarioButton);
		}
		addWidget(mapChoice);
	}
	
	private void enableGameButton(boolean enable) {
		gameButton.setEnabled(enable);
		if(enable) addWidget(gameButton);
		else removeWidget(gameButton);
	}
	
	private void makeGameSettings() {
		int optionX = 0;
		int optionWidth = getWidth() / 2;
		
		saveButton = new LanguageButton(NewGameText.Save_as_default, GUISource.bigFont);
		saveButton.addButtonPressedListener(new IButtonPressedListener() {
			@Override
			public void buttonPressed(ButtonPressedEvent e) {
				boolean noScoreLimit = false;
				if(!options.hasOption(scoreLimitOption)) {
					noScoreLimit = true;
					options.addOption(scoreLimitOption);
				}
				
				boolean noTickets = false;
				if(!options.hasOption(ticketsOption)) {
					noTickets = true;
					options.addOption(ticketsOption);
				}
				
				if(checkInputError()) return;
					
				Settings settings = new Settings(options);
				
				ArrayList<String> selectedMaps = mapChoice.getSelection();
				if(!selectedMaps.isEmpty()) {
					StringBuilder builder = new StringBuilder();
					for(int i = 0; i < selectedMaps.size(); i++) {
						builder.append(selectedMaps.get(i));
						if(i != selectedMaps.size() - 1) builder.append(",");
					}
					settings.addOption(STANDARD_MAP, builder.toString());
				}
				SettingsLoader.saveSettings(DEFAULT_SETTINGS_PATH, settings);
				
				if(noScoreLimit) options.removeOption(scoreLimitOption);
				if(noTickets) options.removeOption(ticketsOption);
			}
		});
		saveButton.setSize(optionWidth, GUISource.bigFont.getHeight());
		saveButton.setXY(optionX, 0);
		addWidget(saveButton);
		
		int optionHeight = OptionsContainer.getOptionHeight() * 11;
		int optionY = saveButton.getHeight();
		
		options = new OptionsContainer(optionWidth, optionHeight, optionX, optionY);
		addWidget(options);
		
		widgetFactory = new OptionWidgetFactory(DEFAULT_SETTINGS_FILE, null);
		defaultSettings = SettingsLoader.loadSettings(DEFAULT_SETTINGS_PATH);
		
		addOptionWidget(SERVERNAME);
		addOptionWidget(RESPAWN);
		addOptionWidget(ENDCOUNTDOWN);
		addOptionWidget(MAXPLAYERS);
		
		final ComboBoxOptionWidget gameModeOption = (ComboBoxOptionWidget)addOptionWidget(GAMEMODE);
		gameModeOption.addChangeListener(new ISelectionChangedListener() {
			@Override
			public void selectionChanged(SelectionChangedEvent selectionChangedEvent) {
				String value = gameModeOption.getValue();
				
				boolean isTDM = value.equals(OptionValues.TeamDeathmatch.toString());
				if(isTDM && !options.hasOption(scoreLimitOption)) options.addOption(scoreLimitOption);
				else if(!isTDM) options.removeOption(scoreLimitOption);
				
				boolean isConquest = value.equals(OptionValues.Conquest.toString());
				if(isConquest && !options.hasOption(ticketsOption)) options.addOption(ticketsOption);
				else if(!isConquest) options.removeOption(ticketsOption);
			}
		});
		
		addOptionWidget(BOTS);
		addOptionWidget(BALANCEDTEAMS);
		addOptionWidget(FRIENDLYFIRE);
		addOptionWidget(AI_LEVEL);
		addOptionWidget(ONLINE);
		
		scoreLimitOption = (TextEditorOptionWidget)addOptionWidget(SCORE_LIMIT, false);
		ticketsOption = (TextEditorOptionWidget)addOptionWidget(TICKETS, false);
		
		gameModeOption.changeValue(LanguageLoader.get(OptionValues.valueOf(defaultSettings.getValueOf(GAMEMODE))));
	}
	
	private OptionWidget addOptionWidget(Lang lang) {
		return addOptionWidget(lang, true);
	}
	
	private OptionWidget addOptionWidget(Lang lang, boolean add) {
		OptionWidget w = widgetFactory.createWidget(lang, defaultSettings.getValueOf(lang));
		if(add) options.addOption(w);
		return w;
	}
	
	private void makePreview() {
		int prevWidth = gameButton.getWidth();
		int prevImageHeight = prevWidth / 4 * 3;
		
		int prevX = getWidth() - prevWidth;
		
		previewImage = new ImageUpdater(prevX, 0, prevWidth, prevImageHeight);
		if(previewImage != null) addWidget(previewImage);
		
		detailsButton = new LanguageButton(NewGameText.Map_Details, GUISource.middleFont);
		detailsButton.setSize(prevWidth, GUISource.middleFont.getHeight());
		detailsButton.setX(prevX);
		detailsButton.addButtonPressedListener(new IButtonPressedListener() {
			@Override
			public void buttonPressed(ButtonPressedEvent e) {
				detailsWindow.init(mapNames.get(currentMapName));
				menu.addWidget(detailsWindow);
			}
		});
		
		detailsButton.setY(gameButton.getHeight());
		previewImage.setY(detailsButton.getY() + detailsButton.getHeight());
		
		detailsWindow = new DetailsWindow(menu, mapDescriptions);
	}
	
	private void makeGameButtons() {
		int buttonWidth = mapChoice.getWidth() / 3;
		int buttonHeight = getHeight() / 10;
		Lang buttonTitle = START_GAME;
		gameButton = new LanguageButton(buttonTitle, GUISource.biggestFont);
		gameButton.addButtonPressedListener(new IButtonPressedListener() {
			@Override
			public void buttonPressed(ButtonPressedEvent e) {
				ArrayList<String> maps = mapChoice.getSelection();
				if(maps.isEmpty()) return;
				if(checkInputError()) return;
				
				ArrayList<String> mapDirNames = new ArrayList<String>();
				for(String mapName : maps) {
					mapDirNames.add(mapNames.get(mapName));
				}
				
				enableGameButton(false);
				int respawn = Integer.valueOf(options.getValueOf(RESPAWN)).intValue();
				int countDown = Integer.valueOf(options.getValueOf(ENDCOUNTDOWN)).intValue();
				int maxPlayers = Integer.valueOf(options.getValueOf(MAXPLAYERS)).intValue();
				boolean bots = Boolean.valueOf(options.getValueOf(BOTS)).booleanValue();
				AILevel level = AILevel.valueOf(options.getValueOf(AI_LEVEL));
				String name = options.getValueOf(SERVERNAME);
				boolean isOnline = Boolean.valueOf(options.getValueOf(ONLINE)).booleanValue();
				GameMode gameMode = GameMode.valueOf(options.getValueOf(GAMEMODE));
				String ip = InitGame.get().getThisIP();
				boolean balancedTeams = Boolean.valueOf(options.getValueOf(BALANCEDTEAMS)).booleanValue();
				boolean friendlyFire = Boolean.valueOf(options.getValueOf(FRIENDLYFIRE)).booleanValue();
				
				int scoreLimit = 0;
				if(gameMode == GameMode.TeamDeathmatch) 
					scoreLimit = Integer.valueOf(options.getValueOf(SCORE_LIMIT)).intValue();
				
				int tickets = 0;
				if(gameMode == GameMode.Conquest) 
					tickets = Integer.valueOf(options.getValueOf(TICKETS)).intValue();
				
				if(isOnline) {
					if(!MasterServerInterface.masterServerReachable()) {
						String message = LanguageLoader.get(MenuText.Server_Not_Reachable);
						MenuMessageWindow mmw = new MenuMessageWindow(menu.getMenuState(), "", message);
						menu.addWidget(mmw);
						return;
					}
				}
				
				GameCycle gameCycle = new GameCycle(mapDirNames, respawn, countDown, maxPlayers, bots, 
						level, name, ip, isOnline, gameMode, scoreLimit, tickets, balancedTeams, friendlyFire);
				InitGame.get().initNewGame(true, gameCycle);
			}
		});
		gameButton.setSize(buttonWidth, buttonHeight);
		gameButton.setXY(getWidth() - buttonWidth, 0);
		enableGameButton(false);
	}
	
	public void setGameStarted(boolean gameStarted) {
		enableGameButton(!gameStarted);
		if(!gameStarted) mapChoice.clearSelection();
	}
	
	private boolean checkInputError() {
		TextInputError error = options.checkTextInput();
		if(error != null) {
			TextInputErrorWindow window = new TextInputErrorWindow(error, menu);
			menu.addWidget(window);
			return true;
		}
		return false;
	}
	
	private void changeLanguage() {
		mapChoice.changeButtonTexts(LanguageLoader.get(NewGameText.Add_Map), LanguageLoader.get(NewGameText.Remove_Map));
	}
}